package uestc.lj.basic.listnode;

/**
 * 两个链表可能有环可能无环
 * 返回两个链表的第一个相交节点
 *
 * @Author:Crazlee
 * @Date:2021/11/22
 */
public class Code06_findFirstInsertNode {

	public static class Node {
		public int value;
		public Node next;

		public Node(int data) {
			this.value = data;
		}
	}

	/**
	 * 寻找两个单链表(可能有环也可能无环)的相交节点
	 * @param head1 单链表1
	 * @param head2 单链表2
	 * @return 相交节点
	 */
	public static Node getIntersectNode(Node head1, Node head2) {
		if (head1 == null || head2 == null) {
			return null;
		}
		Node loopNode1 = getLoopNode(head1);
		Node loopNode2 = getLoopNode(head2);
		if (loopNode1 == null && loopNode2 == null) {
			return noLoop(head1, head2);
		}
		if (loopNode1 != null && loopNode2 != null) {
			return bothLoop(head1, loopNode1, head2, loopNode2);
		}
		return null;
	}

	/**
	 * 寻找链表中第一个入环节点
	 *
	 * @param head 单链表
	 * @return 第一个入环节点，如果无环返回null
	 */
	public static Node getLoopNode(Node head) {
		if (head == null || head.next == null) {
			return head;
		}
		Node slow = head.next;
		Node fast = head.next.next;
		while (slow != fast) {
			if (fast.next == null || fast.next.next == null) {
				return null;
			}
			slow = slow.next;
			fast = fast.next.next;
		}
		fast = head;
		while (slow != fast) {
			slow = slow.next;
			fast = fast.next;
		}
		return slow;
	}

	/**
	 * 当两个链表无环的时候，寻找两个链表相交的节点
	 *
	 * @param head1 单链表1
	 * @param head2 单链表2
	 * @return 链表相交的节点，如果无相交节点则返回null
	 */
	public static Node noLoop(Node head1, Node head2) {
		if (head1 == null || head2 == null) {
			return null;
		}
		return findIntersectionNode(head1, head2);
	}

	/**
	 * 寻找链表中的第一个相交节点
	 *
	 * @param head1
	 * @param head2
	 * @return
	 */
	public static Node findIntersectionNode(Node head1, Node head2) {
		Node cur1 = head1;
		Node cur2 = head2;
		int n = 0;
		while (cur1.next != null) {
			n++;
			cur1 = cur1.next;
		}
		while (cur2.next != null) {
			n--;
			cur2 = cur2.next;
		}
		//判断两个单链表最后节点是否相同，如果不同则一定无相交节点
		if (cur1 != cur2) {
			return null;
		}
		cur1 = n > 0 ? head1 : head2;
		cur2 = cur1 == head1 ? head2 : head1;
		n = Math.abs(n);
		while (n != 0) {
			n--;
			cur1 = cur1.next;
		}
		while (cur1 == cur2) {
			cur1 = cur1.next;
			cur2 = cur2.next;
		}
		return cur1;
	}

	/**
	 * 两个单链表都有环，寻找两个有环链表的相交节点
	 *
	 * @param head1 单链表1
	 * @param loop1 单链表1的入环节点
	 * @param head2 单链表2
	 * @param loop2 单链表2的入环节点
	 * @return 有环链表的相交节点
	 */
	public static Node bothLoop(Node head1, Node loop1, Node head2, Node loop2) {
		Node cur1 = null;
		Node cur2 = null;
		if (loop1 == loop2) {
			cur1 = head1;
			cur2 = head2;
			int n = 0;
			while (cur1 != loop1) {
				n++;
				cur1 = cur1.next;
			}
			while (cur2 != loop2) {
				n--;
				cur2 = cur2.next;
			}
			cur1 = n > 0 ? head1 : head2;
			cur2 = cur1 == head1 ? head2 : head1;
			n = Math.abs(n);
			while (n != 0) {
				n--;
				cur1 = cur1.next;
			}
			while (cur1 != cur2) {
				cur1 = cur1.next;
				cur2 = cur2.next;
			}
			return cur1;
		} else {
			cur1 = loop1.next;
			while (cur1 != loop1) {
				if (cur1 == loop2) {
					return loop1;
				}
				cur1 = cur1.next;
			}
			return null;
		}
	}

}
